/* Filename: relay.c
 * Description: 中继
 * $ sudo ./relay
 * $ <ctrl+alt+f11>
 * $ hello<CR>
 * $ <ctrl+alt+f12>
 * $ world<CR>
 * $ <ctrl+alt+f11>
 * Last modified: humble 20200419 16:55
 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#define TTY1 "/dev/tty11"
#define TTY2 "/dev/tty12"
#define BUFSIZE (1024)

typedef enum{
    STATE_R = 1, //读
    STATE_W, //写
    STATE_Ex, //异常终止
    STATE_T  //结束
}state_t;

typedef struct{
    state_t state;
    int sfd;
    int dfd;
    char buf[BUFSIZE];
    int len;
    int pos;
    char *errstr;
}fsm_t;

static void usage(void);

static void fsm_driver(fsm_t *fsm)
{
    int ret;
    switch(fsm->state){
        case STATE_R:
            fsm->len = read(fsm->sfd, fsm->buf, BUFSIZE);
            if(fsm->len == 0){ //读到文件末尾或者socket已关闭
                fsm->state = STATE_T;
            }
            else if(fsm->len < 0){
                if(errno == EAGAIN){
                    fsm->state = STATE_R;
                }
                else{
                    fsm->errstr = "read()";
                    fsm->state = STATE_Ex;
                }
            }
            else{
                fsm->pos = 0;
                fsm->state = STATE_W;
            }
            break;
        case STATE_W:
            ret = write(fsm->dfd, fsm->buf + fsm->pos, fsm->len);
            if(ret < 0){
                if(errno == EAGAIN){
                    fsm->state = STATE_W;
                }
                else{
                    fsm->errstr = "write()";
                    fsm->state = STATE_Ex;
                }
            }
            else{
                fsm->pos += ret;
                fsm->len -= ret;
                if(fsm->len == 0){
                    fsm->state = STATE_R;
                }
                else{
                    fsm->state = STATE_W;
                }
            }
            break;
        case STATE_Ex:
            //printf("%s\n", fsm->errstr);
            perror(fsm->errstr);
            fsm->state = STATE_T;
            break;
        case STATE_T:
            /* do sth */
            break;
        default:
            /* do sth */
            abort();
            break;
    }
}

static void relay(int fd1, int fd2)
{
    int fd1_save, fd2_save;
    fsm_t fsm12, fsm21;

    fd1_save = fcntl(fd1, F_GETFL);
    fcntl(fd1, F_SETFL, fd1_save|O_NONBLOCK);

    fd2_save = fcntl(fd2, F_GETFL);
    fcntl(fd2, F_SETFL, fd2_save|O_NONBLOCK);

    fsm12.state = STATE_R;
    fsm12.sfd = fd1;
    fsm12.dfd = fd2;

    fsm21.state = STATE_R;
    fsm21.sfd = fd2;
    fsm21.dfd = fd1;

    while(fsm12.state != STATE_T || fsm21.state != STATE_T){
        fsm_driver(&fsm12);
        fsm_driver(&fsm21);
    }

    fcntl(fd1, F_SETFL, fd1_save);
    fcntl(fd2, F_SETFL, fd2_save);
}

int main(int argc, char **argv)
{
    int fd1, fd2;

    if(argc != 1){
        fprintf(stderr, "argc !=1");
        usage();
        exit(1);
    }

    fd1 = open(TTY1, O_RDWR);
    if(fd1 < 0){
        perror("open()");
        exit(1);
    }
    write(fd1, "TTY1\n", 5);


    fd2 = open(TTY2, O_RDWR|O_NONBLOCK);
    if(fd2 < 0){
        perror("open()");
        close(fd1);
        exit(1);
    }
    write(fd2, "TTY2\n", 5);

    relay(fd1, fd2);

    close(fd2);
    close(fd1);
    return 0;
}

static void usage(void)
{
#define USAGE "Usage:\n\
  Helloworld\n"
    printf(USAGE);
}
